#pragma once
#ifdef LIT_OGRE_SUPPORT
#ifndef LIT__OGRE__INITIALISER__HH__
#define LIT__OGRE__INITIALISER__HH__

#include <LitPointer.hpp>
#include <LitExport.hpp>
#include <LitInitialiser.h>
#include <LitSingleton.hpp>
#include <LitOgreState.h>
#include <stack>

namespace Lit
{
	class LIT_API OgrePlatform
	{
		typedef unsigned short Traits;

	protected:
		static const Traits mPlatform;
		static const Traits mArchitecture;

	public:
		OgrePlatform( );
		~OgrePlatform();
		
		static Traits type();
		static Traits architecture();
		
		static bool isLinux();
		static bool isApple();
		static bool isAppleIOS();
		static bool isAndroid();
		static bool isNaCL();
		static bool isWin32();

		static bool is32Bits();
		static bool is64Bits();
		
		struct Window
		{

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
			typedef HWND HandleType;
#elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE
			typedef unsigned int HandleType;
#elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX
			typedef unsigned long HandleType;
#endif
			typedef Ogre::NameValuePairList CreationParameters;

			Window( HandleType hdl, const String &title = "", const size_t width=0, const size_t height = 0 ) 
				: Handle(hdl), Width( width ), Height( height )
			{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
				if( hdl && title.empty() )
				{
					Title.resize( 255 );
				}
				else Title = title;
#else
				Title = title;
#endif
			}
			Window( ) : Title(""), Handle(NULL){}


			Window( const Window& rk ): Title(rk.Title), Handle(rk.Handle), Height(rk.Height), Width(rk.Width){}
			~Window() {}
			
			Window& operator=( const Window& rk ){ Title=rk.Title; Handle=rk.Handle; Width=rk.Width; Height=rk.Height; return *this; }
			
			size_t				Width;
			size_t				Height;
			String			Title;
			HandleType			Handle;
		};

		typedef Window::CreationParameters WindowCreationParams;
		
		static WindowCreationParams getWindowParameters( const Ogre::String& ogre_handle );
		static WindowCreationParams getWindowParameters( const Window& win );
		static Ogre::String getWindowHandle( const Window& win );
	};
	class LIT_API OgreManager : public Singleton<OgreManager>, public Initialiser
	{
	public:
		typedef OgreState				State;
		typedef OgreStatePtr			StatePtr;
		typedef std::stack<StatePtr>	StateStack;
		
	protected:
		StateStack			mStates;
		OgreInitialStatePtr	mInitialState;

		OgreRenderWindowPtr mWindow;

		void loadPlugins( const String &plugins );
		void createViewports();
		Ogre::RenderSystem * createRenderSystem();

	public:
		OgreManager(void);
		~OgreManager(void);
		
		const OgreInitialStatePtr& getInitialState( ) const{ return mInitialState; }
		OgreInitialStatePtr& getInitialState( ){ return mInitialState; }
		
		/*! 
			Initialises OGRE Core ( Ogre::Root )
			\see Ogre::Root initialisation sequence
			\param[in] xml_settings The xml file to load settings from
			\post OGRE is now initialised from a xml settings file
			\post BUT no states have been pushed yet
		*/
		void initialiseOgreBase(const String &xml_settings );
		
		/*! 
			Initialises OGRE Core AND auto creates a render window
			\param[in] windowTitle The title of the window to be created
			\param[in] camera_name The name of the camera to be assigned to the main viewport
			\see Ogre::Root initialisation sequence
			\post OGRE is now initialised from a xml settings file
		*/
		void initialise( const String &xml_settings, const String &windowTitle, const String& camera_name ); 
		
		/*! 
			Initialises OGRE Core AND creates a render window from an existing handle
			\param[in] win The handle of the window
			\param[in] camera_name The name of the camera to be assigned to the main viewport
			\see Ogre::Root initialisation sequence
			\post OGRE is now initialised from a xml settings file and a custom render window
		*/
		void initialise( const String &xml_settings, const OgrePlatform::Window& win, const String &cam_name ); 
		
		/*! 
			Starts the rendering loop
			\see Ogre::Root::startRendering()
			\pre OgreManager is initialised
			\post After calling this function any following code will wait for the loop to end first
			\post Possible end is Alt+F4 - Window close - Exception or User Code
		*/
		bool startRendering( );
		
		bool _renderOneFrame( float dt );
		/*! 
			Shuts down OGRE
			\see Ogre::Root::shutdown()
			\pre OgreManager is initialised
			\post After this call the OGRE engine won't be available, you have to recreate it !
		*/
		void shutdown();
		
		bool hasMoreStates() const; /*!< Is the stack not empty ? */
		void pushState( StatePtr state ); /*!< Pushes a state onto the stack */
		void popState(); /*! Removes a state from the stack and returns it in case you need it */
		void clearStates(){	while( !mStates.empty() ) popState(); mInitialState = NULL; }

		StatePtr& getCurrentState();
		const StatePtr& getCurrentState() const;

		size_t getNumStates() const;
		
		OgreDetails& details();
		const OgreDetails& details() const;
		
		OgreCameraPtr getCamera() const; /*!< Convenience function for accessing getCurrentState()->ogreCamera() */
		OgreRenderWindowPtr getWindow() const; /*!< Convenience function for accessing getCurrentState()->ogreWindow() */
		OgreSceneManagerPtr getSceneManager() const; /*!< Convenience function for accessing getCurrentState()->ogreSceneManager() */
		
		String getLogName() const;  /*!< Engine Default Log Name */

		static String Direct3D11RenderSystem;  /*!< D3D11 RenderSystem internal name */
		static String Direct3D9RenderSystem;	/*!< D3D9 RenderSystem internal name */
		static String OpenGLRenderSystem;		/*!< OGL RenderSystem internal name */
		static String OpenGL3RenderSystem;		/*!< OGL 3+ RenderSystem internal name */
	};
};

#endif
#endif